﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Linq.Expressions;
using CoreLibrary.Paging;
using CoreLibrary.Repo.Sql;
using CoreLibrary.Sql;
using CoreLibrary.Sql.Ext;
using CoreLibrary.Sql.Linq;

namespace CoreLibrary.Repo {

    public abstract class BaseRepository<T> : AbstractLinqSqlService, IRepository<T> where T : class {

        public virtual string TableName { get; private set; }

        private static readonly Func<DataContext, IQueryable<T>>
           LinqGetAll = CompiledQuery.Compile((DataContext context) => context.GetTable<T>());

        private static readonly Func<DataContext, int , int, IQueryable<T>>
           LinqGetMany = CompiledQuery.Compile((DataContext context, int offset, int limit) => 
               context.GetTable<T>()
                      .Skip(offset)
                      .Take(limit));

        protected override void OnInit() {
            base.OnInit();
            TableName = LinqUtil.GetTableAttribute<T>().Name;
        }

        protected override void OnStart() {
            base.OnStart();
            Asserts.NotBlank(TableName, "Table Name can not be blank.");
        }

        public int Count() {
            return Execute(context => context.GetTable<T>().Count());
        }

        public List<T> SearchAll(List<Expression<Func<T, bool>>> expressions) {
            return Execute(context => context.GetTable<T>()
                                             .Where(PredicateUtil.And(expressions))
                                             .ToList()
            );
        }

        public List<T> SearchAny(List<Expression<Func<T, bool>>> expressions) {
            return Execute(context => context.GetTable<T>()
                                             .Where(PredicateUtil.Any(expressions))
                                             .ToList()
            );
        }

        public List<T> GetAll() {
            return Execute(context => LinqGetAll(context).ToList());
        }

        public List<T> GetMany(PagingRequest request) {
            return Execute(context => LinqGetMany(context, request.Offset, request.Limit).ToList());
        }

        public void RemoveAll() {
            Execute(new RemoveAll(TableName));
        }

        public void Remove(T entity) {
            Execute((context, ts) => {
                var table = context.GetTable<T>();
                table.DeleteOnSubmit(entity);
                context.SubmitChanges();
                ts.Complete();
            });
        }

        public void InsertAll(IEnumerable<T> entities) {
            Execute(context => {
                context.InsertAll(entities);
            });
        }

    }

}